import numpy as np
from scipy.signal import hilbert, get_window
from scipy.signal import butter, filtfilt, lfilter
from scipy.fftpack import fft, ifft, fftshift, fftfreq, ifftshift
import pandas as pd
import math
import warnings


'''
    class:
        ButterworthFiltering
        DiagnosisResult
        CalculateWeightScore
    function:
        detrend
        get_array
        add_windows
        freq_select
        iomega
        FFT_spectrogram
        envelope_spectrum
        getXampl
        diagnosis_rule
        calculate_weight_score_3_v2
        rms_feature
'''


def rms_feature(data):
    rms = math.sqrt(sum([x ** 2 for x in data]) / len(data))
    return rms

class ButterworthFiltering:
    """
    调用Scipy.signal.butter中的Butterworth滤波器,属于无限冲激响应（IIR）滤波器
    Calling Butterworth filter in Scipy. signal. butter belongs to
    Infinite Impulse Response (IIR) filter
    """

    def __init__(self, order=4, sampling_frequency=25600):
        self.order = order  # 滤波器的阶数
        self.sampling_frequency = sampling_frequency  # 采样频率

    def low_filtering(self, cutoff_frequency, series):
        """
        Low pass filter
        :param cutoff_frequency: 信号的截止频率
        :param series: 要过滤的目标信号
        :return: 低通滤波过滤之后的信号
        """
        wn = 2 * cutoff_frequency / self.sampling_frequency  # 这是归一化的截止频率
        array = get_array(series)
        b, a = butter(self.order, wn, btype="lowpass", output="ba")
        filtered_array = filtfilt(b, a, array)
        return filtered_array

    def high_filtering(self, cutoff_frequency, series):
        """
        High pass filter
        :param cutoff_frequency: 信号的截止频率
        :param series: 要过滤的目标信号
        :return: 高通滤波过滤之后的信号
        """
        wn = 2 * cutoff_frequency / self.sampling_frequency
        array = get_array(series)
        b, a = butter(self.order, wn, btype="highpass", output="ba")
        filtered_array = filtfilt(b, a, array)
        return filtered_array

    def band_filtering(self, lowcut, highcut, series):
        """
        Bandpass filtering
        :param lowcut:信号截止频率下限
        :param highcut:信号截止频率上限
        :param series:要过滤的目标信号
        :return:带通滤波过滤之后的信号
        """
        low = 2 * lowcut / self.sampling_frequency
        high = 2 * highcut / self.sampling_frequency
        array = get_array(series)
        b, a = butter(self.order, [low, high], btype="bandpass", output="ba")
        filtered_array = filtfilt(b, a, array, irlen=self.sampling_frequency / 2)
        return filtered_array


# 希尔伯特变换求包络
def hilbert_envelop(signal_series):
    """
    调用scipy.fftpack.hilbert接口，信号的希尔伯特转换来进行包络分析
    scipy.signal.hilbert接口可以直接返回hilbert变换后的解析信号，但在本函数中未采用
    :param signal_series: 输入的信号
    :return: ndarry
        输出信号的包络曲线
        希尔伯特变换(Hilbert transform）
        原始信号xr(t)经过Hibert变换，得到信号xi(t).
        原始信号作为实部，变换后的信号作为虚部，构成解析信号 a(t):  a(t)=xr(t)+i*xi(t)
        解析信号的模值，即原始信号xr(t)和转换后xi(t)的平方和开根号，为包络信号A(t):
         A(t)=根下（xr(t)平方+xi(t)平方）
    """
    signal_array = np.array(signal_series)
    hx = hilbert(signal_array)  # 对信号进行希尔伯特变换。
    analytic_signal = signal_array + hx * 1j  # 进行hilbert变换后的解析信号
    hy = np.abs(analytic_signal)  # 计算信号的包络,也就是调制信号的模
    return hy


###################################################
# 去除趋势项
def detrend(signal, deg=2):
    """
    利用numpy.polyfit接口 最小二乘多项式拟合，去除信号中的趋势项
    :param signal: ndarray,list,or pd.Series
            Prepare to remove the trend signal
    :param deg: int
            Degree of the fitting polynomial.
            If you want to remove the DC component of the removed signal, set deg to 0
    :return: array_like
            Detrended signal
    """
    signal_array = np.array(signal)
    x_signal = np.arange(len(signal_array))
    fun = np.polyfit(x_signal, signal_array, deg)
    polyfit_signal = np.polyval(fun, x_signal)
    detrend_signal = signal_array - polyfit_signal
    return detrend_signal


###################################################
# 数据格式转换
def get_array(array):
    """transform data to numpy.array
    """
    if isinstance(array, np.ndarray):
        if len(array.shape) == 1:
            return array
        elif len(array.shape) == 2 and (array.shape[0] == 1 or array.shape[1] == 1):
            return array.reshape(-1)
        else:
            raise TypeError("The dimension of the numpy.array must be 1 or 2")
    elif isinstance(array, (list, pd.Series)):
        array = np.array(array)
        return get_array(array)
    else:
        raise TypeError("Input must be a numpy.array, list or pandas.Series")


###################################################
# 加窗函数
def add_windows(signal, window='hann'):
    signal_array = get_array(signal)
    window_len = len(signal_array)
    window_array = get_window(window, window_len)
    windowed_signal = window_array * signal_array
    return windowed_signal


###################################################
# 滤波，选定滤波范围
def freq_select(fft_signal, sampling_frequency, freq_ratio):
    fft_signal_len = len(fft_signal)
    # df = sampling_frequency / signal_len  # 分辨率
    fftshift_signal = fftshift(fft_signal)
    freq = fftfreq(fft_signal_len, 1 / sampling_frequency)
    fftshift_freq = fftshift(freq)
    low_cutoff_frequency = sampling_frequency / freq_ratio[1]
    high_cutoff_frequency = sampling_frequency / freq_ratio[0]
    # 频率选择的下限为10HZ,上限为1000HZ。即如果采样频率/信号频率小于10HZ，则将频率下限设置为10HZ。
    low_cutoff_frequency = 10 if low_cutoff_frequency < 10 else low_cutoff_frequency
    high_cutoff_frequency = 4000 if high_cutoff_frequency > 4000 else high_cutoff_frequency
    fftshift_freq_index = [False if low_cutoff_frequency < abs(f) else True for f in fftshift_freq]
    fftshift_signal[fftshift_freq_index] = np.complex(0.0, 0.0)
    return fftshift_signal


###################################################
# 积分函数
def iomega(signal, fs, out_type, in_type=3, windows=None, pro_s=1, freq_cutoff=[None, None], return_type='both'):
    """
    IOMEGA is a MATLAB script for converting displacement, velocity, or
    acceleration time-series to either displacement, velocity, or
    acceleration times-series. The script takes an array of waveform data
    (datain), transforms into the frequency-domain in order to more easily
    convert into desired output form, and then converts back into the time
    domain resulting in output (dataout) that is converted into the desired form.
    :param signal: ndarray,list,or pd.Series
        input waveform data of type datain_type
    :param fs: int
            Sampling frequency
    :param out_type: int 1 、2 or 3
            1 – Displacement
            2 – Velocity
            3 – Acceleration
            4 – Shock
    :param in_type: int 1 、2 or 3. Default 3 acceleration signal
            1 – Displacement
            2 – Velocity
            3 – Acceleration
            4 – Shock
    :param windows: string, float, or tuple
        The type of window to create. See scipy.signal.get_window for more details.
    :param pro_s: int
        处理的信号长度，默认是1s
    :param freq_cutoff: list or tuple ,eg[4, 2000]
        积分截止频率
    :param return_type: str
        'signal' 'feature' or 'both'
        if return_type is 'signal', return the waveform of the signal specified by out_type;
        if return_type is 'feature', returns the signal eigenvalue specified by out_type;
        if return_type is 'both', returns a tuple with two elements,
        the first element being the waveform of the signal, which type is ndarray
        and the second element being the eigenvalue of the signal
    :return:

    """
    if in_type not in [1, 2, 3, 4]:
        raise ValueError('Value for datain_type must be a 1, 2 or 3')
    if out_type not in [1, 2, 3, 4]:
        raise ValueError('Value for dataout_type must be 1, 2 or 3')
    if return_type not in ['signal', 'feature', 'both']:
        raise ValueError('return_type must be signal, feature or both')
    # 输入的信号类型转换
    signal_array = np.array(signal)
    # 取前1s的数据加窗处理
    # process_len = round(fs * pro_s)
    # signal_array = signal_array[:process_len].copy()
    signal_len = len(signal_array)
    if windows is not None:
        signal_array = add_windows(signal_array, windows)
    # 去直流
    detrend_signal = signal_array - np.mean(signal_array)
    fft_size = signal_len
    dt = 1 / fs  # dt为采样周期，表示采集样本点之间的时间间隔
    bilateral_freq = fftshift(fftfreq(n=fft_size, d=dt))
    iomega_array = 1j * 2 * np.pi * bilateral_freq
    iomega_exp = out_type - in_type
    # Pad datain array with zeros (if needed)
    if fft_size > signal_len:
        detrend_signal = np.append(detrend_signal, np.zeros(fft_size - signal_len))
    # Transform datain into frequency domain via FFT and shift output (A)
    # so that zero-frequency amplitude is in the middle of the array(instead of the beginning)
    assert fft_size == len(detrend_signal)
    fft_signal = fftshift(fft(detrend_signal))
    fftshift_signal = freq_select(fft_signal, fs, freq_cutoff)
    # fftshift_signal = fftshift(fft(detrend_signal))
    for j in range(fft_size):
        if iomega_array[j] != 0:
            fftshift_signal[j] = fftshift_signal[j] * np.power(iomega_array[j], iomega_exp)
        else:
            fftshift_signal[j] = np.complex(0.0, 0.0)
    # Shift new frequency-amplitude array back to MATLAB format and
    # transform back into the time domain via the inverse FFT
    if return_type != 'signal':
        # 在频域内计算积分信号的RMS
        single_freq_index = bilateral_freq >= 0
        unilateral_fft = fftshift_signal[single_freq_index].copy()
        unilateral_y_amp = abs(unilateral_fft) / len(unilateral_fft)
        rms = np.sqrt((sum(unilateral_y_amp ** 2) - unilateral_y_amp[0] ** 2 / 2 - unilateral_y_amp[-1] ** 2 / 2) / 2)
    ifft_signal = ifft(fftshift(fftshift_signal))
    signal_out = ifft_signal.real
    detrend_signal_out = detrend(signal_out, 2)
    if return_type == 'signal':
        return detrend_signal_out
    else:
        energy_correction_dict = {'hann': 1.63, 'blackman': 1.97, 'kaiser': 1.86, 'hamming': 1.59, 'flattop': 2.26}
        pk_pk = max(detrend_signal_out) - min(detrend_signal_out)
        if windows is not None:  # 对加窗后速度信号的有效值进行能量修正
            if windows in energy_correction_dict.keys():
                rms = rms * energy_correction_dict[windows]
                # pk_pk = pk_pk * amplitude_correction_dict[windows]
            else:
                warnings.warn("The effective value of the speed after windowing is not corrected for amplitude!")
        feature_rms = rms
        feature_pk_pk = pk_pk
        if return_type == 'both':
            return detrend_signal_out, (feature_rms, feature_pk_pk)
        elif return_type == 'feature':
            return feature_rms, feature_pk_pk


###################################################
# 时频域转换
def FFT_spectrogram(signal, fs):
    '''
    :return: 返回FFT之后的x轴数值和y轴数值
    '''
    signal_array = signal
    signal_array_mean = sum(signal_array) / len(signal_array)
    # 信号的均值超过阈值就去直流分量
    if abs(signal_array_mean) > 1e-6:
        signal_array -= signal_array_mean
    del signal_array_mean
    fftshift_signal = fftshift(fft(signal_array))  # FFT后shift到中心点
    fftshift_freq = fftshift(fftfreq(len(signal_array), 1 / fs))  # 获取FFT后频率
    single_fftshift_freq_index = fftshift_freq >= 0
    single_fftshift_y = fftshift_signal[single_fftshift_freq_index].copy()
    x_axis = fftshift_freq[single_fftshift_freq_index].copy()
    # y_axis = abs(single_fftshift_y) * 2 / len(single_fftshift_y)
    y_axis = abs(single_fftshift_y) / len(single_fftshift_y)
    return x_axis, y_axis


def envelope_spectrum(signal_series, fs):
    """
    实现时序包络分析普
    :param signal_series: 采样的时序数据
    :param fs: 采样的频率
    :return: 包络谱的频率 x_frequency 和幅值 y_amplitude (仅仅是大于零的频率)
    """
    len_signal = len(signal_series)  # 计算采样数据的长度
    hy = hilbert_envelop(signal_series)  # 计算信号的包络
    hy -= np.mean(hy)
    y_dual_amplitude = abs(fftshift(fft(hy))) * 2 / len_signal  # 计算幅值
    x_dual_freq = fftshift(fftfreq(len_signal, 1 / fs))  # 计算频率
    x_freq = x_dual_freq[x_dual_freq >= 0]
    y_amplitude = y_dual_amplitude[x_dual_freq >= 0]
    assert len(x_freq) == len(y_amplitude)
    return x_freq, y_amplitude


###################################################
# calculate the xAmpl information
def getXampl(x_axis, y_axis, fs, rote):
    '''
    :param data: data 需要计算倍频的一列数据
    :param fs: 采样频率
    :param rote: 转速
    :return: 返回计算的倍频 DataFrame类型
    '''

    return_df = pd.DataFrame()
    # 转速判断

    if rote is None:
        max_xAmpl = np.max(y_axis)
        index = np.where(y_axis == max_xAmpl)
        rote = x_axis[index][0] * 60
    ratio = x_axis[1] - x_axis[0]

    for i in range(1, 11):
        energy_range_1 = int((rote / 60 * i - 5) / ratio)
        energy_range_2 = int((rote / 60 * i + 5) / ratio)

        energy = max(y_axis[energy_range_1:energy_range_2], default=0)
        return_df[str(i) + "xAmpl"] = [round(energy, 4)]
    return return_df


def diagnosis_rule(axmpl, impulse_mean):
    """
    :param axmpl: 1xAmpl--10xAmpl
    :return: diagnosis_result, maintenance_advice
            正常值上限       注意      告警      备注
    不平衡     0.6           0.7     0.8       一倍频与10个倍频的比值
    不对中     0.3           0.6      1        二倍频与一倍频比值
    轴承故障    0.8           2       3         冲击平均值大小
    """
    result_list = ['不平衡', '不对中', '轴承故障']
    maintenance_list = ['调整动平衡，排除使能部分平衡情况', '联轴器重新找正',
                        '加大润滑，调整轴承装配或者更换轴承']
    # 不平衡
    try:
        imbalance_rate = float(axmpl["1xAmpl"] / axmpl.sum(axis=1))
        if impulse_mean > 0.2:
            imbalance_w, imbalance_score = calculate_weight_score_3_v2(0.6, 0.7, 0.8, imbalance_rate)
        else:
            imbalance_w, imbalance_score = 1, 85
    except:
        imbalance_w, imbalance_score = 1, 88
    # 不对中
    try:
        centering_rate = float(axmpl["2xAmpl"] / axmpl["1xAmpl"])
        centering_w, centering_score = calculate_weight_score_3_v2(0.3, 0.6, 1, centering_rate)
    except:
        centering_w, centering_score = 1, 88
    # 轴承故障
    try:
        if impulse_mean is not None:
            bearing_w, bearing_score = calculate_weight_score_3_v2(0.8, 2, 3, impulse_mean)
        else:
            bearing_w, bearing_score = 1, 88
    except:
        bearing_w, bearing_score = 1, 88

    rate_list = [imbalance_score, centering_score, bearing_score]
    weight_list = [imbalance_w, centering_w, bearing_w]
    return_index = rate_list.index(min(rate_list))
    diagnosis_weight = weight_list[return_index]
    diagnosis_score = min(rate_list)
    if diagnosis_score >= 80:
        diagnosis_level = '健康'
    elif diagnosis_score >= 60:
        diagnosis_level = '注意'
    else:
        diagnosis_level = '警告'
    if diagnosis_score < 60:
        maintenance_advice = maintenance_list[return_index]
        diagnosis_result = result_list[return_index]
    else:
        maintenance_advice = ''
        diagnosis_result = '健康'
    return imbalance_w, imbalance_score, centering_w, centering_score, bearing_w, bearing_score, diagnosis_result, maintenance_advice, diagnosis_level, diagnosis_score, diagnosis_weight


class DiagnosisResult:
    def __init__(self, imbalance_w, imbalance_score,
                 centering_w, centering_score,
                 bearing_w, bearing_score,
                 diagnosis_result, maintenance_advice,
                 diagnosis_level, diagnosis_score, diagnosis_weight):
        '''
        :param diagnosis_result: 诊断结论（'不平衡', '不对中', '轴承故障'）
        :param maintenance_advice: 维修意见
        :param diagnosis_level: 故障水平（健康、注意、警告）
        :param diagnosis_score: 诊断分数
        '''
        self.diagnosisResult = diagnosis_result
        self.MaintenanceAdvice = maintenance_advice
        self.diagnosisLevel = diagnosis_level
        self.diagnosisScore = diagnosis_score
        self.diagnosisWeight = diagnosis_weight
        self.ResultAll = {'平衡状态': {'score': imbalance_score, 'weight': imbalance_w},
                          '对中状态': {'score': centering_score, 'weight': centering_w},
                          '轴承状态': {'score': bearing_score, 'weight': bearing_w}
                          }


# 计分函数
def calculate_weight_score_3_v2(a1, a2, a3, x):
    """
    a1:下限值
    a2:上限值
    a3:报警值
    return:
        weight,score
    """
    print('-----', a1, a2, a3, x)

    b = 1 / 4 * a1 + 3 / 4 * a2
    try:
        if x < b:
            w = 1
            score = 90
        elif b <= x < a2:
            w = math.exp(((x - b) / b) ** 2)
            k = (80 - 90) / (a2 - b)
            score = 90 - abs(k * (x - b))
        elif a2 <= x < a3:
            w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2)
            k = (60 - 80) / (a3 - a2)
            score = 80 - abs(k * (x - a2))
        else:  # x>a3 报警值
            print('dd')
            w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2)
            k = (60 - 80) / (a3 - a2)
            score = 80 - abs(k * (x - a2))
            if score > 0:
                score = score
            else:
                score = 0
    except:
        x = a3
        w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2)
        k = (60 - 80) / (a3 - a2)
        score = 80 - abs(k * (x - a2))
        if score > 0:
            score = score
        else:
            score = 0
    return round(w, 2), round(score, 2)


class CalculateWeightScore:
    def calculate_weight_score_3_v1(self, a0, a1, a2, x):
        """
        a0:下报警值
        a1：下限值
        a2: 上限值
        """
        b1 = 3 / 4 * a1 + 1 / 4 * a2
        b2 = 1 / 4 * a1 + 3 / 4 * a2
        try:
            if b1 <= x < b2:
                w = 1
                score = 90.0
            elif b2 <= x < a2:
                w = math.exp(((x - b2) / b2) ** 2)
                k = (80 - 90) / (a2 - b2)
                score = 90 - abs(k * (x - b2))
            elif x >= a2:
                w = math.exp(((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2)
                k = (80 - 90) / (a2 - b2)
                score = 90 - abs(k * (x - b2))
                if score > 0:
                    score = score
                else:
                    score = 0.0
            elif a1 <= x < b1:
                w = math.exp(((x - b1) / b1) ** 2)
                k = (90 - 80) / (b1 - a1)
                score = 90 - abs(k * (x - b1))
            elif a0 <= x < a1:
                w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2)
                k = (80 - 60) / (a1 - a0)
                score = 80 - abs(k * (x - a1))
            else:  # x<a0 故障报警
                w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2 + ((x - a0) / a0) ** 2)
                k = (80 - 60) / (a1 - a0)
                score = 80 - abs(k * (x - a1))
                if score > 0:
                    score = score
                else:
                    score = 0.0
        except:
            x = a2
            w = math.exp(((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2)
            k = (80 - 90) / (a2 - b2)
            score = 90 - abs(k * (x - b2))
            if score > 0:
                score = score
            else:
                score = 0
        return w, score

    def calculate_weight_score_3_v2(self, a1, a2, a3, x):
        """
        a1:下限值
        a2:上限值
        a3:报警值
        return:
            weight,score
        """
        check_result = self.data_check(x)
        if check_result is True:
            return 1, 88
        b = 1 / 4 * a1 + 3 / 4 * a2
        try:
            if x < b:
                w = 1
                score = 90
            elif b <= x < a2:
                w = math.exp(((x - b) / b) ** 2)
                k = (80 - 90) / (a2 - b)
                score = 90 - abs(k * (x - b))
            elif a2 <= x < a3:
                w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2)
                k = (60 - 80) / (a3 - a2)
                score = 80 - abs(k * (x - a2))
            else:  # x>a3 报警值
                w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2)
                k = (60 - 80) / (a3 - a2)
                score = 80 - abs(k * (x - a2)) > 0
                if score > 0:
                    score = score
                else:
                    score = 0
        except:
            x = a3
            w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2)
            k = (60 - 80) / (a3 - a2)
            score = 80 - abs(k * (x - a2)) > 0
            if score > 0:
                score = score
            else:
                score = 0
        return w, score

    def calculate_weight_score_4_v1(self, a1, a2, a3, a4, x):
        """
        parameters:
            a1:下限值
            a2:上限值
            a3:报警值
            a4:跳机值
            x:当前值
        return:
            权重weight,根据当前输入值,来确定当前数据应该占的权重
        """
        try:
            b = 1 / 4 * a1 + 3 / 4 * a2
            if x < b:
                w = 1
                score = 90
            elif b <= x < a2:
                w = math.exp(((x - b) / b) ** 2)
                k = (80 - 90) / (a2 - b)
                score = 90 - abs(k * (x - b))
            elif a2 <= x < a3:
                w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2)
                k = (60 - 80) / (a3 - a2)
                score = 80 - abs(k * (x - a2))
            elif a3 <= x < a4:
                w = math.exp(((x - b) / b) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2 + ((x - a3) / (x - a4)) ** 2)
                k = (0 - 60) / (a4 - a3)
                score = 60 - abs(k * (x - a3))
            else:  # x>a4 跳机值
                w = 0
                score = 0
            return w, score
        except:
            return 1, 90

    def calculate_weight_score_4_v2(self, a0, a1, a2, a3, x):
        """
        parameters:
            a0:下限报警值
            a1:下限值
            a2:上限值
            a3:报警值
            x:当前值
        return:
            权重weight,根据当前输入值,来确定当前数据应该占的权重
        """
        b1 = 3 / 4 * a1 + 1 / 4 * a2
        b2 = 1 / 4 * a1 + 3 / 4 * a2
        if b1 <= x < b2:
            w = 1
            score = 90
        elif b2 <= x <= a2:
            w = math.exp(((x - b2) / b2) ** 2)
            k = (80 - 90) / (a2 - b2)
            score = 90 - abs(k * (x - b2))
        elif a2 <= x < a3:
            w = math.exp(((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2)
            k = (60 - 80) / (a3 - a2)
            score = 80 - abs(k * (x - a2))
        elif x >= a3:
            w = math.exp(((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2)
            k = (60 - 80) / (a3 - a2)
            score = 80 - abs(k * (x - a2))
            if score > 0:
                score = score
            else:
                score = 0
        elif a1 <= x < b1:
            w = math.exp(((x - b1) / b1) ** 2)
            k = (90 - 80) / (b1 - a1)
            score = 90 - abs(k * (x - b1))
        elif a0 <= x < a1:
            w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2)
            k = (80 - 60) / (a1 - a0)
            score = 80 - abs(k * (x - a1))
        else:  # x<a0 故障报警
            w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2 + ((x - a0) / a0) ** 2)
            k = (80 - 60) / (a1 - a0)
            score = 80 - abs(k * (x - a1))
            if score > 0:
                score = score
            else:
                score = 0
        return w, score

    def calculate_weight_score_4_v3(self, a_1, a0, a1, a2, x):
        """
        parameters:
            a_0:下限跳机值
            a0:下限报警值
            a1:下限值
            a2:上限值
        return:
            权重,得分
        """
        b1 = 3 / 4 * a1 + 1 / 4 * a2
        # b2 = 1/4*a1+3/4*a2
        if x >= b1:
            w = 1
            score = 90
        elif a1 <= x < b1:
            w = math.exp(((x - b1) / b1) ** 2)
            k = (90 - 80) / (b1 - a1)
            score = 90 - abs(k * (x - b1))
        elif a0 <= x < a1:
            w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2)
            k = (80 - 60) / (a1 - a0)
            score = 80 - abs(k * (x - a1))
        elif a_1 <= x < a0:
            w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2 + ((x - a0) / a0) ** 2 + ((x - a0) / (x - a_1)) ** 2)
            k = (60 - 0) / (a0 - a_1)
            score = 60 - abs(k * (x - a0))
        else:  # x<a_1 故障报警
            w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2 + ((x - a0) / a0) ** 2 + ((x - a0) / (x - a_1)) ** 2)
            score = 0
        return w, score

    def calculate_weight_score_5(self, a0, a1, a2, a3, a4, x):
        """
        parameters:
            a0:下限报警值
            a1:下限值
            a2:上限值
            a3:上限报警值
            a4：跳机值
        return:
            权重,得分
        """
        b1 = 3 / 4 * a1 + 1 / 4 * a2
        b2 = 1 / 4 * a1 + 3 / 4 * a2
        if b1 <= x < b2:
            w = 1
            score = 90
        elif b2 <= x < a2:
            w = math.exp(((x - b2) / b2) ** 2)
            k = (80 - 90) / (a2 - b2)
            score = 90 - abs(k * (x - b2))
        elif a2 <= x < a3:
            w = math.exp(((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2)
            k = (60 - 80) / (a3 - a2)
            score = 80 - abs(k * (x - a2))
        elif a3 <= x < a4:
            w = math.exp(((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2 + ((x - a3) / (x - a4)) ** 2)
            k = (0 - 60) / (a4 - a3)
            score = 60 - abs(k * (x - a3))
        elif x >= a4:
            w = 0
            score = 0
        elif a1 <= x < b1:
            w = math.exp(((x - b1) / b1) ** 2)
            k = (90 - 80) / (b1 - a1)
            score = 90 - abs(k * (x - b1))
        elif a0 <= x < a1:
            w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2)
            k = (80 - 60) / (a1 - a0)
            score = 80 - abs(k * (x - a1))
        else:  # x<a0 故障报警
            w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2 + ((x - a0) / a0) ** 2)
            k = (80 - 60) / (a1 - a0)
            score = 80 - abs(k * (x - a1))
            if score > 0:
                score = score
            else:
                score = 0
        return w, score

    def calculate_weight_score_6(self, a_1, a0, a1, a2, a3, a4, x):
        """
        parameters:
            a0:下限报警值
            a1:下限值
            a2:上限值
            a3:上限报警值
            a4：跳机值
        return:
            权重,得分
        """
        b1 = 3 / 4 * a1 + 1 / 4 * a2
        b2 = 1 / 4 * a1 + 3 / 4 * a2
        try:
            if b1 <= x < b2:
                w = 1
                score = 90
            elif b2 <= x < a2:
                w = math.exp(((x - b2) / b2) ** 2)
                k = (80 - 90) / (a2 - b2)
                score = 90 - abs(k * (x - b2))
            elif a2 <= x < a3:
                w = math.exp(((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2)
                k = (60 - 80) / (a3 - a2)
                score = 80 - abs(k * (x - a2))
            elif a3 <= x < a4:
                w = math.exp(
                    ((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2 + ((x - a3) / (x - a4)) ** 2)
                k = (0 - 60) / (a4 - a3)
                score = 60 - abs(k * (x - a3))
            elif x >= a4:
                w = math.exp(
                    ((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2 + ((x - a3) / (x - a4)) ** 2)
                score = 0
            elif a1 <= x < b1:
                w = math.exp(((x - b1) / b1) ** 2)
                k = (90 - 80) / (b1 - a1)
                score = 90 - abs(k * (x - b1))
            elif a0 <= x < a1:
                w = math.exp(((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2)
                k = (80 - 60) / (a1 - a0)
                score = 80 - abs(k * (x - a1))
            elif a_1 <= x < a0:
                w = math.exp(
                    ((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2 + ((x - a0) / a0) ** 2 + ((x - a0) / (x - a_1)) ** 2)
                k = (60 - 0) / (a0 - a_1)
                score = 60 - abs(k * (x - a0))
            else:  # x<a_1 故障报警
                w = math.exp(
                    ((x - b1) / b1) ** 2 + ((x - a1) / a1) ** 2 + ((x - a0) / a0) ** 2 + ((x - a0) / (x - a_1)) ** 2)
                score = 0
        except:
            x = a4 + 0.1  #
            w = math.exp(
                ((x - b2) / b2) ** 2 + ((x - a2) / a2) ** 2 + ((x - a3) / a3) ** 2 + ((x - a3) / (x - a4)) ** 2)
            score = 0
        return w, score

    def data_check(self, data):
        '''
        温度：<=-10 or >=200，或数据缺失
        '''
        if data <= -10 or data >= 200:
            return True
        else:
            return False
